#include "USerial.h"

SerialDlg::SerialDlg()
{
    CtrlLayout(*this);
    Sizeable().Zoomable();
    sendBtn.Enable(false);
    display.Highlight("log");

	openBtn <<= THISBACK(OpenBtnClick);
	sendBtn <<= THISBACK(SendBtnClick);
	baudList << THISBACK(BaudListClick);
	portList.WhenPush = THISBACK(PortListClick);

	//setup baud rate, 设置常用波特率
	Upp::Vector<int> baudRateList = {
            300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 
            28800, 38400, 56000, 57600, 115200, 128000, 230400,
            256000, 460800, 500000, 512000, 600000, 750000, 921600,
            1000000, 1500000, 2000000, 2500000, 3000000, 3500000
	};
	for (int i = 0; i < baudRateList.GetCount(); i++)
	{
		baudList.Add(baudRateList[i]);
	}
    FreshPortList();

    //Register Event Handler
    MyRegisterEvent(EVENT_SERIAL_DATA_RECV, THISBACK(OnSerialDataRecv));
    MyRegisterEvent(EVENT_HW_HOTPLUG, THISBACK(OnHotPlug));
    MyRegisterEvent(EVENT_SERIAL_OPENED, THISBACK(OnPortOpened));
    MyRegisterEvent(EVENT_SERIAL_CLOSED, THISBACK(OnPortClosed));
	MyRegisterEvent(EVENT_QUIT, THISBACK(OnQuit));
}

SerialDlg::~SerialDlg()
{
	if (logFile.IsOpen())
	{
		logFile.Close();
	}
}

void SerialDlg::OnQuit(const MyEvent &e)
{
	if (logFile.IsOpen())
	{
		logFile.Close();
	}
}

void SerialDlg::OnPortClosed(const MyEvent &e)
{
	if (e.event_data[0] == portName)
	{
		portName.Clear();
	}
}

void SerialDlg::OnPortOpened(const MyEvent &e)
{
    if (!opened)
    {
        return;
    }
    if (e.event_data[0] == portName)
    {
        if (e.event_data[1] != "0") 
        {
            //open failed
            opened = false;
            openBtn.SetLabel("打开");
            sendBtn.Enable(false);
        }
    }
}
void SerialDlg::OnHotPlug(const MyEvent &e)
{
    GuiLock _;
    FreshPortList();
}
void SerialDlg::OnSerialDataRecv(const MyEvent &e)
{
#if 0
	RichText txt;
	RichPara::CharFormat fmt;
	(Font&)fmt = Serif(20);

	//数据中寻找换行符，然后分割成多个段落
	int start = 0;
	int end = 0;
	while (end < e.event_data.GetCount())
	{
		if (e.event_data[end] == '\n')
		{
			RichPara para;
			para.Cat(e.event_data.Mid(start, end - start + 1), fmt);
			txt.Cat(para);
			start = end + 1;
		}
		end++;
	}
	RichPara para_end;
	if (start < e.event_data.GetCount())	
	{
		para_end.Cat(e.event_data.Mid(start), fmt);
		txt.Cat(para_end);
	}

	//mainDisplay.AppendText(txt);
#endif
	GuiLock __;

	ONCELOCK 
    {
        if (display.IsTimestampEnabled())
        {
            display.AppendAndScroll(AsString(e.event_time));
			logFile << AsString(e.event_time);
        }
        lastTime = e.event_time;

	}
	//find the serial port
    if (portName != e.event_data[0])
    {
        return;
    }
	if (~hexOption)
	{
		//hex mode
		//hex 数据换行规则：超过一定时间间隔(比如50ms)，换行
		if (e.event_time - lastTime > 0.05)
		{
			if (display.IsTimestampEnabled())
			{
				display.AppendAndScroll("\n" + AsString(e.event_time));
				logFile << "\n" + AsString(e.event_time);
			}
			lastTime = e.event_time;
		}
		//将数据转换成hex格式
		display.AppendAndScroll(AsHexString(e.event_data.ToString()));
		logFile << AsHexString(e.event_data.ToString());
	}
	else
	{
		//text mode
		//找到换行符，在换行符处分割字符串，并添加time前缀
		int start = 0;
		int end = 0;
		int len = e.event_data[1].GetCount();
		if (forceAddTime)
		{
			forceAddTime = false;
			display.AppendAndScroll(AsString(e.event_time));
			logFile << AsString(e.event_time);
		}
		while (end < len)
		{
			if ((e.event_data[1][end] == '\n') && (end != (len-1)))
			{
				display.AppendAndScroll(e.event_data[1].Mid(start, end - start + 1));
				logFile << e.event_data[1].Mid(start, end - start + 1);
				start = end + 1;
				//append time prefix
				if (display.IsTimestampEnabled())
				{
					display.AppendAndScroll(AsString(e.event_time));
					logFile << AsString(e.event_time);
				}
			}
			end++;
		}
		if (start != end)
		{
			display.AppendAndScroll(e.event_data[1].Mid(start, end - start + 1));
			logFile << e.event_data[1].Mid(start, end - start + 1);
		}
		if (e.event_data[1].EndsWith("\n"))
		{
			forceAddTime = true;
		}
	}
}
void SerialDlg::BaudListClick()
{
    //baudrate changed, reopen port
    if (opened)
    {
        OpenSerial(true);
    }
}
void SerialDlg::PortListClick()
{
    if (opened)
    {
        //Close port
        OpenSerial(false);
        OpenSerial(true);
    }
}

void SerialDlg::SendBtnClick()
{
    //post EVENT_SERIAL_SEND_DATA event
    if (!opened)
    {
        return;
    }
	//从sendEdit中获取数据，发送到串口
	//1. 要区分字符串发送还是hex发送
	//2. 对于\n，需要转换成\r\n
	//3. 对于hex发送，需要将字符串转换成hex数据
	String data = sendEdit.Get();
	if (data.IsEmpty())
	{
		return;
	}
	//获取当前串口
	if (hexSend)
	{
		//hex发送
		//将字符串中的空格删除
		data.Replace(" ", "");
		String sendData = ScanHexString(data);
		MyPostEvent(EVENT_SERIAL_SEND_DATA, {portName, sendData});
	}
	else
	{
		//字符串发送
		//将\n转换成\r\n
		data.Replace("\n", "\r\n");
		MyPostEvent(EVENT_SERIAL_SEND_DATA, {portName,data});
	}
}

void SerialDlg::OpenSerial(bool open)
{
	if (open)
	{
		//open serial port
		String name = portList.Get();
		portName = name.Left(name.Find(' '));
        String baudrate;
        baudrate << baudList.Get();
        MyPostEvent(EVENT_OPEN_SERIAL, {portName, baudrate});
        openBtn.SetLabel("关闭");
        sendBtn.Enable(true);
		//create log file, portname_time.log
		String filename = "log/" + portName;
		Time t = GetSysTime();
		filename << Format("_%d-%d-%d_%d-%d-%d.log", t.year, t.month, t.day, t.hour, t.minute, t.second);
		logFile.Open(filename, FileStream::CREATE | FileStream::READWRITE);
		logFile.Flush();
	}
	else
	{
		//close serial port
        if (portName.GetLength())
        {
            MyPostEvent(EVENT_CLOSE_SERIAL, {portName});
        }
        openBtn.SetLabel("打开");
        sendBtn.Enable(false);
		if (logFile.IsOpen())
		{
			logFile.Close();
		}
	}
}
void SerialDlg::OpenBtnClick()
{
    //反转
    opened = !opened;
    OpenSerial(opened);
}

void SerialDlg::FreshPortList()
{
	std::vector<SerialPortInfo> ports = CSerialPortInfo::availablePortInfos();
	
	portList.Clear();

	for (int i = 0; i < ports.size(); i++)
	{
		String port = ports[i].portName;
		port << " " << ports[i].description;
		portList.Add(i, port);
	}
	{
		//如果正在打开的串口拔出了，需要关闭；如果之前打开的串口插入了，需要重新打开
		if (opened && portName.GetLength())
		{
			for (int i = 0; i < ports.size(); i++)
			{
				if (portName.IsEqual(&ports[i].portName[0]))
				{
					//打开串口
					portList <<= i;
					OpenSerial(true);
					return ;
				}
			}
			//拔出了，需要关闭
			OpenSerial(false);
		}

	}
}
